/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


#include "server.h"
#include "acceptor.h"
#include "coid/svc/coidsvc.h"

#include "coid/comm/binstream/stdstream.h"
#include "coid/comm/binstream/textstream.h"

namespace coid {

////////////////////////////////////////////////////////////////////////////////
void* RootCoid::logdump()
{
    post_info(0) << "started log dump thread" << EOM;

    SVCLOG::logpointer lgp( SRVROOTA._log, 0, 0 );
    dynarray<logstream*> ls;

    charstr buf;

#ifdef SYSTYPE_MSVC
    bofstream sout("server.log");
#else
    stdoutstream sout;
#endif

    uint gcnt=0;

    while( !is_exit_signalled() )
    {
        //check if we need to restart the graveyard
        if( _graveyards <= 0 )
        {
            if( ++gcnt >= 10 )
            {
                if( _graveyards <= 0 )
                    spawn_graveyard_loop();
                gcnt = 0;
            }
        }
        else
            gcnt = 0;

        lgp.read( ls, 16 );
        if( ls.size() > 0 )
        {
            for( uint i=0; i<ls.size(); ++i )
            {
                buf.reset();
                SVCLOG::print_msg( buf, *ls[i] );

                buf << "\n";

                sout.write_token_raw(buf);
            }

            sout << BINSTREAM_FLUSH;

            SRVROOTA._log.dump();
        }
        else
            sysMilliSecondSleep(100);
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void* RootCoid::gravehandle()
{
    post_info(0) << "started object graveyard thread" << EOM;

    ++_graveyards;

    while( !is_exit_signalled() )
    {
        try {
            SRVROOTA.check_autodestroy();
            SRVROOTA.prune_dead();
        }
        catch(...)
        {
            break;
        }
        sysMilliSecondSleep(100);
    }

    --_graveyards;

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd RootCoid::list_cmds( const token& cmd, dynarray<charstr>& cmds ) const
{
    if( cmd.is_empty() )
    {
        cmds.push("acceptor restart");
        cmds.push("drop ID");
        cmds.push("find CLASS [NAME]");
        cmds.push("finds CLASS [NAME]");
        cmds.push("findi CLASS [NAME]");
        cmds.push("graveyard");
        cmds.push("server (restart|shutdown)");
        cmds.push("version");
		cmds.push("uptime");
#ifdef _DEBUG
        cmds.push("lamx");
#endif
        return CoidNode::list_cmds( cmd, cmds );
    }
    else if( cmd == "acceptor" )
        cmds.push("acceptor restart\n\trestart coid acceptor");
    else if( cmd == "drop" )
        cmds.push("drop ID\n\tdrop object with id ID");
    else if( cmd == "find" )
        cmds.push("find CLASS [NAME]\n\tfind objects of class CLASS, optionally named NAME");
    else if( cmd == "finds" )
        cmds.push("finds CLASS [NAME]\n\tfind services of user class CLASS, optionally named NAME");
    else if( cmd == "findi" )
        cmds.push("findi CLASS [NAME]\n\tfind instances of user class CLASS, optionally named NAME");
    else if( cmd == "graveyard" )
        cmds.push("graveyard\n\tstart a graveyard thread");
    else if( cmd == "server" )
        cmds.push("server (restart|shutdown)\n\trestart or shutdown server");
    else if( cmd == "version" )
        cmds.push("version\n\tprint server version");
    else if( cmd == "uptime" )
        cmds.push("uptime\n\tprint how long coid has been running");
#ifdef _DEBUG
    else if( cmd == "lamx" )
        cmds.push("lamx\n\tprint all locked mutexes");
#endif
    else
        return CoidNode::list_cmds( cmd, cmds );

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd RootCoid::dispatch_cmd( const token& cmd, command_tokens& arg, txtstream& reply )
{
    if( cmd == "prld" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        
    }
    if( cmd == "drop" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        dynarray<uint> target;
        opcd e = SRVROOTA.parse_path( _id, _id, arg[0], target );
        if(e)  return e;

        for( uint i=0; i<target.size(); ++i )
        {
            SRVROOTA.add_to_dead_list( target[i] );
        }
        return 0;
    }
    else if( cmd == "find" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        dynarray<uint> lst;
        version v(0);
        opcd e = SRVROOTA._objmgr.find_nodes_of_class( 0, arg[0], 0, arg[1], lst );
        if( e == 0  &&  lst.size() > 0 )
        {
            TreeMgr::Attribs at;
            for( uint i=0; i<lst.size(); ++i )
            {
                SRVROOTA._objmgr.get_node_attribs( lst[i], at );
                reply << lst[i] << " " << at.name;
            }
        }
        else if(!e)
            e = ersNOT_FOUND;
        return e;
    }
    else if( cmd == "finds" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        dynarray<uint> lst;
        version v(0);
        opcd e = SRVROOTA._objmgr.service_find_all( arg[0], lst );
        if( e == 0  &&  lst.size() > 0 )
        {
            TreeMgr::Attribs at;
            for( uint i=0; i<lst.size(); ++i )
            {
                SRVROOTA._objmgr.get_node_attribs( lst[i], at );
                reply << lst[i] << " " << at.name;
            }
        }
        else if(!e)
            e = ersNOT_FOUND;
        return e;
    }
    else if( cmd == "findi" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        dynarray<uint> lst;
        version v(0);
        opcd e = SRVROOTA._objmgr.serviceinst_find_all( arg[0], lst );
        if( e == 0  &&  lst.size() > 0 )
        {
            TreeMgr::Attribs at;
            for( uint i=0; i<lst.size(); ++i )
            {
                SRVROOTA._objmgr.get_node_attribs( lst[i], at );
                reply << lst[i] << " " << at.name;
            }
        }
        else if(!e)
            e = ersNOT_FOUND;
        return e;
    }
    else if( cmd == "graveyard" )
    {
        spawn_graveyard_loop();
        return 0;
    }
/*    else if (command[0] == "digraph")
    {
        LNID root=0;
        if (!command[1].is_empty())
            root = command[1].toul();
        return SRVROOTA.gen_digraph (root, reply);
    }*/
    else if( cmd == "version" )
    {
        charstr buf;
        reply << "coid version: " << GserverVersion.get_version(buf);
        return 0;
    }
    else if( cmd == "server" )
    {
        if( arg[0].is_empty() )
            return stream_cmds_list( cmd, reply );

        post_alert(0) << "remote client requested shutdown" << EOM;

        if( arg[0] == "restart" || arg[1] == "shutdown" )
        {
            post_alert(0) << "spawning new coid process" << EOM;

            // stop acceptors first so that the new process can continue
            dynarray<uint> lst;
            opcd e = SRVROOTA._objmgr.find_nodes_of_class( 0, CoidAcceptorCoid::GET_INTERFACE(), "", lst );
            if(!e)
            {
                for( uint i=0; i<lst.size(); ++i )
                    SRVROOTA.detach( lst[i], 0 );
            }

            sysSleep(1);

            if( arg[0] == "shutdown" )
                SRVROOTA.cancel(0);
            else
                SRVROOTA.cancel(1);
        }
        else
            return stream_cmds_list( cmd, reply );

//        PostQuitMessage(0);
        return 0;
    }
    else if( cmd == "acceptor" )
    {
        if (arg[0] == "restart")
        {
            dynarray<uint> lst;
            opcd e = SRVROOTA._objmgr.find_nodes_of_class( 0, CoidAcceptorCoid::GET_INTERFACE(), "", lst );
            if(!e)
            {
                for( uint i=0; i<lst.size(); ++i )
                    SRVROOTA.detach( lst[i], 0 );
            }

            sysSleep(2);

            int port = SRVROOTA.get_coidaccp_port();
            return SRVROOTA.try_start_coid_acceptor(port);
        }
        else
            return stream_cmds_list( cmd, reply );
    }
#ifdef _DEBUG
    else if( cmd == "lamx" )
    {
		uint time = 0;
		if( arg._cmd.len() )
			time = arg._cmd.touint();
		charstr out;
		opcd e = create_list( out, time, true );
		if( out.len() )
			reply.xwrite_raw( out.ptr(), out.len() );
		return e;
    }
#endif
    else if( cmd == "uptime" )
    {
		uint time = uint(SINGLETON(HPTIMER).ftime() * 0.001);
		uint m = time / 60;
		uint h = m / 60;
		uint d = h / 24;
		m -= h*60;
		h -= d*24;
        reply << "Up " << d << " days, " << h << ":" << num_right0<2>(m);
		return 0;
    }

    return CoidNode::dispatch_cmd( cmd, arg, reply );
}



} //namespace coid
